//
//  IMConversationManager.m
//  MLIM
//
//  Created by molon on 15/6/30.
//  Copyright (c) 2015年 molon. All rights reserved.
//

#import "IMConversationManager.h"
#import <AudioToolbox/AudioToolbox.h>
#import "IMCommon.h"

NSString * const MLIM_IMConversation_UpdateDetail_Notification = @"com.molon.MLIM_IMConversation_Updated_Notification";
NSString * const MLIM_IMConversation_NewMessages_Notification = @"com.molon.MLIM_IMConversation_NewMessages_Notification";
NSString * const MLIM_IMConversationManager_Clean_Notification = @"com.molon.MLIM_IMConversationManager_Clean_Notification";

/*
 本地化存储的文件结构是
 Document -> MLIM -> clientID ->
 (
 IMConversation.plist
 BackupMessages -> 会话ID.plist 备份的最后20条消息
 )
 */

NSInteger const kMLIM_MaxBackupConvCount = 600;
NSInteger const kMLIM_MaxDisplayConvCount = 300;
NSInteger const kMLIM_MaxBackupMessageCount = 20;

NSString *const kMLIM_BaseDirName = @"MLIM";
NSString *const kMLIM_ConvsFileName = @"IMConversation";
NSString *const kMLIM_BackupMsgDirName = @"BackupMessages";
//未读语音记录文件名字
NSString *const kMLIM_IMUnheardAudioMessageIdsFileName = @"IMUnheardAudioMessageIds";

#define RETURN_BOOL_WHEN_NIL_CURRENT if ([NSString IsNilOrEmpty:self.currentClientId]) return NO;
#define RETURN_WHEN_NIL_CURRENT if ([NSString IsNilOrEmpty:self.currentClientId]) return;
#define RETURN_NIL_WHEN_NIL_CURRENT if ([NSString IsNilOrEmpty:self.currentClientId]) return nil;


//更新和读取 最后20条消息和音频未读标记的 以及会话列表 串行队列
static dispatch_queue_t mlim_archiver_ioqueue() {
    static dispatch_queue_t mlim_archiver_ioqueue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        mlim_archiver_ioqueue = dispatch_queue_create("com.molon.MLIM_ArchiverIOQueue", DISPATCH_QUEUE_SERIAL);
    });
    
    return mlim_archiver_ioqueue;
}

@interface IMConversationManager()

@property (nonatomic, strong) NSMutableDictionary *imConversations;
@property (nonatomic, copy) NSString *currentClientId;
@property (nonatomic, strong) NSMutableArray *imUnheardAudioMessageIds;

@property (nonatomic, strong) NSCache *cacheMessages;

@end

@implementation IMConversationManager

+ (instancetype)manager
{
    static IMConversationManager *_sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[[self class] alloc]init];
    });
    return _sharedInstance;
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(imConvsUpdated:) name:MLIM_IMConversation_UpdateDetail_Notification object:nil];
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
    }
    return self;
}

#pragma mark - notification
- (void)applicationWillEnterForeground
{
    //更新下所有的头像
    [self updateAllIMConversationDetailsWithCallback:nil];
}

- (void)imConvsUpdated:(NSNotification*)notification
{
    NSDictionary *userInfo = notification.userInfo;
    if ([userInfo[@"archiver"] boolValue]) {
        DLOG(@"保存会话列表");
        [self archiverCurrentIMConvs];
    }
    
    if (self.needUpdateIMUnreadCountSumBlock) {
        self.needUpdateIMUnreadCountSumBlock([self unreadCount]);
    }
}

#pragma mark - getter
- (NSCache *)cacheMessages
{
    if (!_cacheMessages) {
        _cacheMessages = [NSCache new];
        _cacheMessages.countLimit = 3;
    }
    return _cacheMessages;
}

- (NSMutableDictionary *)imConversations
{
    if (!_imConversations) {
        _imConversations = [NSMutableDictionary new];
    }
    return _imConversations;
}

- (NSMutableArray *)imUnheardAudioMessageIds
{
    if (!_imUnheardAudioMessageIds) {
        _imUnheardAudioMessageIds = [NSMutableArray new];
    }
    return _imUnheardAudioMessageIds;
}


#pragma mark - archiver
- (NSString*)currentDirPath
{
    RETURN_NIL_WHEN_NIL_CURRENT
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *baseDirPath = [paths[0] stringByAppendingPathComponent:kMLIM_BaseDirName];
    NSString *currentDirPath = [baseDirPath stringByAppendingPathComponent:self.currentClientId];
    if (![[NSFileManager defaultManager] fileExistsAtPath:currentDirPath]){
        [[NSFileManager defaultManager] createDirectoryAtPath:currentDirPath withIntermediateDirectories:YES attributes:nil error:NULL];
        [[NSFileManager defaultManager] addSkipBackupAttributeToItemAtPath:baseDirPath];
    }
    return currentDirPath;
}

- (NSString*)imConvsFilePath
{
    RETURN_NIL_WHEN_NIL_CURRENT
    
    return [[self currentDirPath]stringByAppendingPathComponent:kMLIM_ConvsFileName];
}

- (NSString*)imUnheardAudioMessageIdsFilePath
{
    RETURN_NIL_WHEN_NIL_CURRENT
    
    return [[self currentDirPath]stringByAppendingPathComponent:kMLIM_IMUnheardAudioMessageIdsFileName];
}

- (NSString*)backupMsgsFilePathOfIMConvId:(NSString*)imConvId
{
    RETURN_NIL_WHEN_NIL_CURRENT
    NSAssert(![NSString IsNilOrEmpty:imConvId], @"imConvId不可为空");
    
    NSString *backupMsgDirPath = [[self currentDirPath]stringByAppendingPathComponent:kMLIM_BackupMsgDirName];
    if (![[NSFileManager defaultManager] fileExistsAtPath:backupMsgDirPath]){
        [[NSFileManager defaultManager] createDirectoryAtPath:backupMsgDirPath withIntermediateDirectories:YES attributes:nil error:NULL];
        [[NSFileManager defaultManager] addSkipBackupAttributeToItemAtPath:backupMsgDirPath];
    }
    return [backupMsgDirPath stringByAppendingPathComponent:imConvId];
}

- (void)archiverCurrentIMConvsWithCallback:(IMBooleanResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(NO,nil);
        }
        return;
    }
    
    void(^archiverBlock)(NSData *,NSString *) = ^(NSData *data,NSString *path){
        dispatch_async(mlim_archiver_ioqueue(), ^{
            BOOL result = [data writeToFile:path atomically:YES];
            if (callback) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    callback(result,nil);
                });
            }
        });
    };
    
    
    NSString *path = [self imConvsFilePath];
    
    if (self.imConversations.count<=kMLIM_MaxBackupConvCount) {
        archiverBlock([NSKeyedArchiver archivedDataWithRootObject:self.imConversations],path);
        return;
    }
    
    //找到最新的kMLIM_MaxBackupConvCount条
    NSArray *sortedValidIMConversations = [[self.imConversations allValues] sortedArrayUsingComparator:^NSComparisonResult(IMConversation *imConv1,IMConversation *imConv2) {
        int64_t timeOffset = imConv2.lastMessageTime - imConv1.lastMessageTime;
        timeOffset = MAX(timeOffset, NSOrderedAscending);
        timeOffset = MIN(timeOffset, NSOrderedDescending);
        return timeOffset;
    }];
    sortedValidIMConversations = [sortedValidIMConversations subarrayWithRange:NSMakeRange(0, kMLIM_MaxBackupConvCount)];
    //整理成字典
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    for (IMConversation *conv in sortedValidIMConversations) {
        dict[conv.conversationId] = conv;
    }
    
    archiverBlock([NSKeyedArchiver archivedDataWithRootObject:dict],path);
}

//直接不考虑结果
- (void)archiverCurrentIMConvs
{
    [self archiverCurrentIMConvsWithCallback:nil];
}

- (void)archiverIMMessages:(NSArray*)imMessages toIMConvId:(NSString*)imConvId callback:(IMBooleanResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(NO,nil);
        }
        return;
    }
    NSAssert(![NSString IsNilOrEmpty:imConvId], @"imConvId不可为空");
    if (!imMessages) {
        return;
    }
    
    if (imMessages.count>kMLIM_MaxBackupMessageCount) {
        //只塞入最后的20条
        imMessages = [imMessages subarrayWithRange:NSMakeRange(imMessages.count-kMLIM_MaxBackupMessageCount, kMLIM_MaxBackupMessageCount)];
    }
    
    NSArray *cacheMessages = [self.cacheMessages objectForKey:imConvId];
    if (cacheMessages&&![cacheMessages isEqual:imMessages]) {
        //如果当前缓存里存了就更新下
        [self.cacheMessages setObject:imMessages forKey:imConvId];
    }
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:imMessages];
    NSString *path = [self backupMsgsFilePathOfIMConvId:imConvId];
    dispatch_async(mlim_archiver_ioqueue(), ^{
        BOOL result = [data writeToFile:path atomically:YES];
        if (callback) {
            dispatch_async(dispatch_get_main_queue(), ^{
                callback(result,nil);
            });
        }
    });
}

- (void)archiverCurrentIMUnheardAudioIdsWithCallback:(IMBooleanResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(NO,nil);
        }
        return;
    }
    
    if (!self.imUnheardAudioMessageIds) {
        return;
    }
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.imUnheardAudioMessageIds];
    NSString *path = [self imUnheardAudioMessageIdsFilePath];
    dispatch_async(mlim_archiver_ioqueue(), ^{
        BOOL result = [data writeToFile:path atomically:YES];
        if (callback) {
            dispatch_async(dispatch_get_main_queue(), ^{
                callback(result,nil);
            });
        }
    });
}

#pragma mark - private helper
- (void)updateAllIMConversationDetailsWithCallback:(IMBooleanResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(NO,nil);
        }
        return;
    }
    
    if (self.imConversations.count>0) {
        //主动更新所有的会话的用户信息
        //所有的clientId加起来
        //TODO:因为现在都是单聊，所以这样没有什么问题
        NSMutableSet *idsSet = [NSMutableSet set];
        for (IMConversation *imConv in [self.imConversations allValues]) {
            [idsSet addObjectsFromArray:imConv.clientIds];
        }
        if (idsSet.count>0) {
            [IMUM updateOrAddClientIds:idsSet callback:^(BOOL succeeded, NSError *error) {
                if (succeeded) {
                    //如果有更新这里面就已经通知会话刷新了
                    [self configureDetailForIMConversations:[self.imConversations allValues] callback:^(BOOL succeeded, NSError *error) {
                        if (callback) {
                            callback(succeeded,error);
                        }
                    }];
                    return;
                }
                if (callback) {
                    callback(succeeded,error);
                }
            }];
            return;
        }
    }
    
    if (callback) {
        callback(YES,nil);
    }
}

//配置这些会话的详情，此方法不会刷新User缓存，只会去获取没有的User
- (void)configureDetailForIMConversations:(NSArray*)imConvs callback:(IMBooleanResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(NO,nil);
        }
        return;
    }
    
    //TODO: 我们现在没有群聊，暂时这样搞一搞，有群聊的话需要找到其中某些成员，拼出来头像，或者再做从网上获取的回调机制
    NSMutableDictionary *convClientIdMap = [NSMutableDictionary dictionaryWithCapacity:imConvs.count];
    
    //所有的clientId加起来
    NSMutableSet *idsSet = [NSMutableSet set];
    for (IMConversation *imConv in imConvs) {
        //找到这个会话所使用的clientId
        NSMutableArray *clientIds = [imConv.clientIds mutableCopy];
        if (clientIds.count<=0) {
            continue;
        }
        [idsSet addObjectsFromArray:clientIds];
        
        NSString *clientId;
        [clientIds removeObject:IMCM.currentClientId];
        //找到第一个不是自己的用户
        clientId = [clientIds firstObject];
        if (!clientId) {
            clientId = IMCM.currentClientId;
        }
        convClientIdMap[imConv.conversationId] = clientId;
    }
    
    if (idsSet.count<=0) {
        if (callback) {
            callback(YES,nil);
        }
        return;
    }
    
    [IMUM imUsersWithClientIds:idsSet callback:^(NSDictionary *dict, NSError *error) {
        if (dict.count<=0||error) {
            if (callback) {
                callback(NO,error);
            }
            return;
        }
        
        for (IMConversation *imConv in imConvs) {
            IMUser *imUser = dict[convClientIdMap[imConv.conversationId]];
            if (imUser) {
                imConv.name = imUser.name;
                imConv.avatarURL = imUser.avatarURL;
            }
//            else{
//                if (callback) {
//                    callback(NO,error);
//                }
//                return;
//            }
        }
        
        //通知出去
        [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_UpdateDetail_Notification object:nil userInfo:@{@"archiver":@(YES),@"imConvs":imConvs}];
        
        if (callback) {
            callback(YES,nil);
        }
    }];
}

#pragma mark - outcall
- (NSInteger)unreadCount
{
    return [[[self sortedValidIMConversations] valueForKeyPath:@"@sum.unreadCount"]integerValue];
}

- (void)clean
{
    //同步下
    dispatch_sync(mlim_archiver_ioqueue(), ^{});
    
    self.currentDisplayIMConvId = nil;
    self.currentClientId = nil;
    self.imConversations = nil;
    self.imUnheardAudioMessageIds = nil;
    
    //通知会话刷新
    [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_UpdateDetail_Notification object:nil userInfo:nil];
    //通知正在显示的会话消失
    [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversationManager_Clean_Notification object:nil userInfo:nil];
}

- (void)resetWithCurrentClientId:(NSString*)currentClientId
{
    NSAssert(![NSString IsNilOrEmpty:currentClientId], @"currentClientId不可为空");
    
    //先clean了
    [self clean];
    
    //重新取一下会话列表，并且通知UI
    self.currentClientId = currentClientId;
    
    NSString *path = [self imConvsFilePath];
    
    //解序列化会话
    __block NSDictionary *dict = nil;
    dispatch_sync(mlim_archiver_ioqueue(), ^{
       dict = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    });
    self.imConversations = [dict mutableCopy];
    
    //解序列化未读音频,同步解即可
    path = [self imUnheardAudioMessageIdsFilePath];
    __block NSArray *imUnheardAudioMessageIds = nil;
    dispatch_sync(mlim_archiver_ioqueue(), ^{
        imUnheardAudioMessageIds = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
    });
    self.imUnheardAudioMessageIds = [imUnheardAudioMessageIds mutableCopy];
    
    //清空缓存
    [self.cacheMessages removeAllObjects];
    
    //通知会话刷新,不管下面如何，先刷新了
    [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_UpdateDetail_Notification object:nil userInfo:nil];
    
    [self updateAllIMConversationDetailsWithCallback:nil];
    
}


//根据id获取会话
- (void)imConvWithIMConvId:(NSString*)convId callback:(IMConversationResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(nil,nil);
        }
        return;
    }
    NSAssert(![NSString IsNilOrEmpty:convId], @"imConvId不可为空");
    
    IMConversationResultBlock preCallback = ^(IMConversation *imConv,NSError *error){
        if (imConv&&![imConv isHaveDetail]) {
            [self configureDetailForIMConversations:@[imConv] callback:^(BOOL succeeded, NSError *error) {
                if (!succeeded) {
                    if (callback) {
                        callback(nil,error);
                    }
                    return;
                }
                
                if (callback) {
                    callback(imConv,nil);
                }
            }];
        }else{
            if (callback) {
                callback(imConv,error);
            }
        }
    };
    
    IMConversation *imConv = self.imConversations[convId];
    if (!imConv) {
        NSAssert(self.imConversationSearchBlock, @"必须实现imConversationSearchBlock");
        
        IMConversationSearchCallbackBlock block = ^(IMConversation *imConv,NSError *error){
            if (imConv) {
                //我们要加到缓存里，用不用另外一回事
                self.imConversations[imConv.conversationId] = imConv;
                //存档下吧
                [self archiverCurrentIMConvs];
            }
            preCallback(imConv,error);
        };
        
        self.imConversationSearchBlock(IMConversationSearchTypeConvId,convId,block);
        
        return;
    }
    
    preCallback(imConv,nil);
}

//根据成员IDs获取会话
- (void)imConvWithClientIds:(NSArray*)clientIds callback:(IMConversationResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(nil,nil);
        }
        return;
    }
    
    NSMutableSet *idsSet = [NSMutableSet setWithArray:clientIds];
    if ([idsSet containsObject:self.currentClientId]) {
        [idsSet removeObject:self.currentClientId];
    }
    
    NSAssert(idsSet.count>=1, @"clientIds传递有误");
    NSMutableArray *members = [[idsSet allObjects]mutableCopy];
    [members insertObject:self.currentClientId atIndex:0]; //其实这个只是为了让本人的clientId排在第一位，这样如果会话不存在建立的时候其名字会在前面。
    
    IMConversationResultBlock preCallback = ^(IMConversation *imConv,NSError *error){
        if (imConv&&![imConv isHaveDetail]) {
            [self configureDetailForIMConversations:@[imConv] callback:^(BOOL succeeded, NSError *error) {
                if (!succeeded) {
                    if (callback) {
                        callback(nil,error);
                    }
                    return;
                }
                
                if (callback) {
                    callback(imConv,nil);
                }
            }];
        }else{
            if (callback) {
                callback(imConv,error);
            }
        }
    };
    
    //遍历本地的会话，检测其clientIds set是否相等,相等的话就代表找到了
    NSSet *currentSet = [NSSet setWithArray:members];
    for (IMConversation *imConv in [self.imConversations allValues]) {
        if ([currentSet isEqualToSet:[NSSet setWithArray:imConv.clientIds]]) {
            //找到了
            preCallback(imConv,nil);
            return;
        }
    }
    
    NSAssert(self.imConversationSearchBlock, @"必须实现imConversationSearchBlock");
    
    IMConversationSearchCallbackBlock block = ^(IMConversation *imConv,NSError *error){
        if (imConv) {
            //我们要加到缓存里，用不用另外一回事
            self.imConversations[imConv.conversationId] = imConv;
            //存档下吧
            [self archiverCurrentIMConvs];
        }
        preCallback(imConv,error);
    };
    
    self.imConversationSearchBlock(IMConversationSearchTypeMemberIds,members,block);
}

//获取排序过的有效的会话列表
- (NSArray*)sortedValidIMConversations
{
    NSAssert(kMLIM_MaxDisplayConvCount<=kMLIM_MaxBackupConvCount, @"kMLIM_MaxDisplayConvCount必须小于kMLIM_MaxBackupConvCount");
    return [self sortedValidIMConversationsWithMaxCount:kMLIM_MaxDisplayConvCount];
}

- (NSArray*)sortedValidIMConversationsWithMaxCount:(NSInteger)maxCount
{
    RETURN_NIL_WHEN_NIL_CURRENT
    
    NSMutableArray *array = [NSMutableArray array];
    for (IMConversation *imConv in [self.imConversations allValues]) {
        if ([imConv isValidToDisplay]) {
            [array addObject:imConv];
        }
    }
    
    //排序
    NSArray *sortedValidIMConversations = [array sortedArrayUsingComparator:^NSComparisonResult(IMConversation *imConv1,IMConversation *imConv2) {
        int64_t timeOffset = imConv2.lastMessageTime - imConv1.lastMessageTime;
        timeOffset = MAX(timeOffset, NSOrderedAscending);
        timeOffset = MIN(timeOffset, NSOrderedDescending);
        return timeOffset;
    }];
    
    if (sortedValidIMConversations.count>maxCount) {
        sortedValidIMConversations = [sortedValidIMConversations subarrayWithRange:NSMakeRange(0, maxCount)];
    }
    return sortedValidIMConversations;
}

//获取会话备份消息
- (NSArray*)cacheMessagesWithIMConvId:(NSString*)convId
{
    RETURN_NIL_WHEN_NIL_CURRENT
    
    //从cache里找，找不到就从文件里取
    __block NSArray *cacheMessages = [self.cacheMessages objectForKey:convId];
    if (!cacheMessages) {
        //从文件里取出来
        NSString *path = [self backupMsgsFilePathOfIMConvId:convId];
        dispatch_sync(mlim_archiver_ioqueue(), ^{
            NSArray *backupMessages = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
            //TODO:可能需要去重，但是最好是在插入时候
            NSTimeInterval currentTime = [[NSDate date]timeIntervalSince1970];
            //如果某消息已经过了20秒还没发送出去那就认为它是发送失败了
            for (IMMessage *imMsg in backupMessages) {
                if (imMsg.status == IMMessageStatusSending&&currentTime-imMsg.sendTimestamp>20) {
                    imMsg.status = IMMessageStatusSendFailed;
                }
            }
            cacheMessages = backupMessages;
        });
        
        if (!cacheMessages) {
            cacheMessages = @[];
        }
        
        //存入cache里
        [self.cacheMessages setObject:cacheMessages forKey:convId];
    }
    
    return cacheMessages;
}

//清零未读数目
- (void)clearUnreadCountWithIMConvId:(NSString*)convId
{
    RETURN_WHEN_NIL_CURRENT
    NSAssert(![NSString IsNilOrEmpty:convId], @"imConvId不可为空");
    
    [self imConvWithIMConvId:convId callback:^(IMConversation *imConv, NSError *error) {
        if (!imConv) {
            return;
        }
        if (imConv.unreadCount!=0) {
            imConv.unreadCount = 0;
            
            //通知会话刷新
            [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_UpdateDetail_Notification object:nil userInfo:@{@"archiver":@(YES),@"imConvs":@[imConv]}];
        }
    }];
}

//添加新消息到会话
- (void)addNewIMMessages:(NSArray*)imMessages toIMConvId:(NSString*)convId
{
    RETURN_WHEN_NIL_CURRENT
    NSAssert(![NSString IsNilOrEmpty:convId], @"imConvId不可为空");
    
    //首先我们要更新这个会话的最后信息
    [self imConvWithIMConvId:convId callback:^(IMConversation *imConv, NSError *error) {
        if (!imConv) {
            return;
        }
        
        //这些消息里是否有未听音频需要添加
        BOOL isAddedUnheardAudio = NO;
        
        NSMutableArray *needSendMsgs = [NSMutableArray array];
        
        //更新会话信息
        for (IMMessage *imMsg in imMessages) {
            NSAssert(![imMsg isKindOfClass:[IMTextDraftMessage class]], @"addNewIMMessages方法不支持草稿消息");
            
            imMsg.conversationId  = convId;
            
            //未读数目
            if (![imMsg.clientId isEqualToString:self.currentClientId]) {
                //不是正在显示的聊天会话就增加未读数目
                if ([imConv.conversationId isEqualToString:self.currentDisplayIMConvId]) {
                    imConv.unreadCount = 0;
                }else{
                    imConv.unreadCount++;
                }
                
                //震动
                AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
                
                if ([imMsg isKindOfClass:[IMAudioMessage class]]) {
                    //记录下未读状态
                    [self.imUnheardAudioMessageIds addObject:imMsg.messageId];
                    isAddedUnheardAudio = YES;
                }
            }else{
                if (imMsg.status==IMMessageStatusSending) {
                    imMsg.localMessageId = [NSString stringWithFormat:@"%@-%@-%.2f-%d",imMsg.clientId,convId,imMsg.sendTimestamp,arc4random()%1000];
                    [needSendMsgs addObject:imMsg];
                }
            }
        }
        
        if (isAddedUnheardAudio) {
            //即时缓存吧，这个数据量不会太大
            [self archiverCurrentIMUnheardAudioIdsWithCallback:nil];
        }
        
        //归档信息缓存
        NSMutableArray *cacheMessages = [[self cacheMessagesWithIMConvId:convId]mutableCopy];
        [cacheMessages addObjectsFromArray:imMessages];
        [self archiverIMMessages:cacheMessages toIMConvId:convId callback:^(BOOL succeeded, NSError *error) {
            if (succeeded) {
                //执行send方法
                for (IMMessage *imMsg in needSendMsgs) {
                    [self sendIMMessage:imMsg];
                }
            }
        }];
        
        imConv.lastMessage = [imMessages lastObject];
        
        //通知会话刷新
        [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_UpdateDetail_Notification object:nil userInfo:@{@"archiver":@(YES),@"imConvs":@[imConv]}];
        //通知新消息
        [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_NewMessages_Notification object:nil userInfo:@{@"imConv":imConv,@"imMessages":imMessages}];
        
        //做下本地推送，为了能让信息在进到后台的那刹那不会看不到提醒。
        if (self.alertBodyForLocalNotificationBlock) {
            for (IMMessage *imMsg in imMessages) {
                if (!IMISME(imMsg.clientId)) {
                    //提取一些信息，username，和描述
                    [IMUM imUserWithClientId:imMsg.clientId callback:^(IMUser *imUser, NSError *error) {
                        NSString *content = self.alertBodyForLocalNotificationBlock(imUser,imMsg);
                        if (![NSString IsNilOrEmpty:content]) {
                            UILocalNotification *localNotification = [[UILocalNotification alloc]init];
                            if (localNotification) {
                                localNotification.alertBody = content;
                                localNotification.userInfo = @{@"isForMLIM":@(YES)};
                                localNotification.soundName = UILocalNotificationDefaultSoundName;
                                [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
                            }
                        }
                    }];
                }
            }
            
        }
        
    }];
}

- (void)configureTextDraftMessageWithContent:(NSString*)content toIMConvId:(NSString*)convId
{
    IMTextDraftMessage *draftTextMsg = [IMTextDraftMessage new];
    draftTextMsg.content = content;
    draftTextMsg.clientId = self.currentClientId;
    draftTextMsg.conversationId = convId;
    draftTextMsg.sendTimestamp = [[NSDate date]timeIntervalSince1970];
    draftTextMsg.status = IMMessageStatusNormal;
    
    [self configureLastMessage:draftTextMsg toIMConvId:convId];
}

- (void)configureLastMessage:(IMMessage*)lastMessage toIMConvId:(NSString *)convId
{
    RETURN_WHEN_NIL_CURRENT
    NSAssert(![NSString IsNilOrEmpty:convId], @"imConvId不可为空");
    
    [self imConvWithIMConvId:convId callback:^(IMConversation *imConv, NSError *error) {
        if (!imConv) {
            return;
        }
        
        imConv.lastMessage = lastMessage;
        
        //通知会话刷新
        [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_UpdateDetail_Notification object:nil userInfo:@{@"archiver":@(YES),@"imConvs":@[imConv]}];
    }];
}

- (void)replyNewIMMessage:(IMMessage*)imMessage toIMConvId:(NSString*)convId
{
    RETURN_WHEN_NIL_CURRENT
    NSAssert(![NSString IsNilOrEmpty:convId], @"imConvId不可为空");
    
    imMessage.clientId = self.currentClientId;
    imMessage.conversationId = convId;
    imMessage.sendTimestamp = [[NSDate date]timeIntervalSince1970];
    imMessage.status = IMMessageStatusSending;
    [self addNewIMMessages:@[imMessage] toIMConvId:convId];
}

- (void)reSendIMMessage:(IMMessage*)imMsg
{
    RETURN_WHEN_NIL_CURRENT
    NSAssert(![NSString IsNilOrEmpty:imMsg.conversationId]&&![NSString IsNilOrEmpty:imMsg.localMessageId], @"IMMessage必须有其会话ID和localMessageId");
    
    //首先我们要更新这个会话的最后信息
    [self imConvWithIMConvId:imMsg.conversationId callback:^(IMConversation *imConv, NSError *error) {
        if (!imConv) {
            return;
        }
        
        NSMutableArray *cacheMutableMessages = [[self cacheMessagesWithIMConvId:imMsg.conversationId]mutableCopy];
        //存档里有重复的，就先移除
        for (NSInteger i=0; i<cacheMutableMessages.count; i++) {
            IMMessage *msg = cacheMutableMessages[i];
            if ([msg.localMessageId isEqualToString:imMsg.localMessageId]) {
                [cacheMutableMessages removeObjectAtIndex:i];
                break;
            }
        }
        
        //设置新的发送时间和设置发送中状态
        imMsg.sendTimestamp = [[NSDate date]timeIntervalSince1970];
        imMsg.status = IMMessageStatusSending;
        
        //添加到最后
        [cacheMutableMessages addObject:imMsg];
        
        //归档信息缓存
        [self archiverIMMessages:cacheMutableMessages toIMConvId:imMsg.conversationId callback:nil];
        
        imConv.lastMessage = imMsg;
        
        //通知会话刷新
        [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_UpdateDetail_Notification object:nil userInfo:@{@"archiver":@(YES),@"imConvs":@[imConv]}];
        
        
        [self sendIMMessage:imMsg];
    }];
}

- (void)sendIMMessage:(IMMessage*)imMsg
{
    RETURN_WHEN_NIL_CURRENT
    
    NSAssert(![NSString IsNilOrEmpty:imMsg.conversationId], @"IMMessage必须有其会话ID");
    NSAssert(self.sendIMMessageBlock,@"必须实现sendIMMessageBlock");
    
    if (imMsg.status!=IMMessageStatusSending) {
        imMsg.status = IMMessageStatusSending;
    }
    
    WEAKSELF
    IMSendMessageCallbackBlock callback = ^(IMMessage *message,BOOL succeed,NSError *error){
        STRONGSELF
        message.status = succeed?IMMessageStatusNormal:IMMessageStatusSendFailed;
        if (succeed&&[message isKindOfClass:[IMAudioMessage class]]) {
            AudioServicesPlaySystemSound(1004);
        }
        
        NSMutableArray *cacheMutableMessages = [[self cacheMessagesWithIMConvId:imMsg.conversationId]mutableCopy];
        
        //找到这个信息然后存档
        for (NSInteger i=0; i<cacheMutableMessages.count; i++) {
            IMMessage *msg = cacheMutableMessages[i];
            if ([msg.localMessageId isEqualToString:message.localMessageId]) {
                [cacheMutableMessages replaceObjectAtIndex:i withObject:message];
                [sSelf archiverIMMessages:cacheMutableMessages toIMConvId:imMsg.conversationId callback:nil];
                return;
            }
        }
    };
    self.sendIMMessageBlock(imMsg,callback);
}

//通过网络获取在某某信息ID之前的数据
- (void)webIMMessagesWithIMConvId:(NSString*)convId beforeIMMessage:(IMMessage*)imMessage count:(NSInteger)count callback:(IMArrayResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(nil,nil);
        }
        return;
    }
    NSAssert(![NSString IsNilOrEmpty:convId], @"imConvId不可为空");
    
    NSAssert(self.imMessagesBlock, @"imMessagesBlock不可为空");
    
    IMArrayResultBlock preCallback = ^(NSArray *objects,NSError *error){
        //直接返回吧，去重和备份靠VC里去调用好些，对性能有好处
        //放到下一个runloop里是为了避免在VC里 tableView的willDisplay里直接插入新行产生的崩溃问题。
        dispatch_async(dispatch_get_main_queue(), ^{
            if (callback) {
                callback(objects,error);
            }
        });
    };
    
    self.imMessagesBlock(convId,imMessage,count,preCallback);
}


- (void)updateOrAddIMConversations:(NSArray*)imConvs callback:(IMBooleanResultBlock)callback
{
    if ([NSString IsNilOrEmpty:self.currentClientId]) {
        if (callback) {
            callback(NO,nil);
        }
        return;
    }
    
    NSMutableArray *updatedIMConvs = [NSMutableArray arrayWithCapacity:imConvs.count];
    
    for (IMConversation *imConv in imConvs) {
        //检测是否已经存在
        if (self.imConversations[imConv.conversationId]) {
            //更新
            CHILD(IMConversation, self.imConversations[imConv.conversationId]).clientIds = imConv.clientIds;
            //检测新的有最后调戏且最后一条不是草稿的情况下
            if (imConv.lastMessage&&![CHILD(IMConversation, self.imConversations[imConv.conversationId]).lastMessage isKindOfClass:[IMTextDraftMessage class]]) {
                CHILD(IMConversation, self.imConversations[imConv.conversationId]).lastMessage = imConv.lastMessage;
            }
        }else{
            self.imConversations[imConv.conversationId] = imConv;
        }
        
        [updatedIMConvs addObject:self.imConversations[imConv.conversationId]];
    }
    
    //更新其会话头像和名字
    [self configureDetailForIMConversations:updatedIMConvs callback:^(BOOL succeeded, NSError *error) {
        
        if (callback) {
            callback(succeeded,error);
        }
    }];
}

- (BOOL)isUnhearedForAudioMessageId:(NSString*)messageId
{
    RETURN_BOOL_WHEN_NIL_CURRENT
    return [self.imUnheardAudioMessageIds containsObject:messageId];
}


- (void)signHearedForAudioMessageId:(NSString*)messageId
{
    RETURN_WHEN_NIL_CURRENT
    
    if (![self isUnhearedForAudioMessageId:messageId]) {
        return;
    }
    
    [self.imUnheardAudioMessageIds removeObject:messageId];
    //即时缓存吧
    [self archiverCurrentIMUnheardAudioIdsWithCallback:nil];
    
    //通知会话列表更新UI
    [[NSNotificationCenter defaultCenter]postNotificationName:MLIM_IMConversation_UpdateDetail_Notification object:nil userInfo:nil];
}


@end
